﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Windows.Forms;
using System.Xml;
using DeVierKoeden.Dynamicweb.CreateSite.Extensions;
using DeVierKoeden.Dynamicweb.CreateSite.Models;
using DeVierKoeden.Dynamicweb.CreateSite.Rizzo;
using Microsoft.Web.Administration;

namespace DeVierKoeden.Dynamicweb.CreateSite
{
  /// <summary>
  /// Main form to configure new sites and delete existing ones.
  /// </summary>
  public partial class MainForm : Form, ILogger
  {
    private ConfigureRizzoForm _rizzoForm;
    private readonly IgnoreFolders _ignoreFolderDialog = new IgnoreFolders();

    /// <summary>
    /// Initializes a new instance of the MainForm class.
    /// </summary>
    public MainForm()
    {
      InitializeComponent();
    }

    #region Public properties

    public Site SiteForVersionUpgrade => SitesForUpgrade.SelectedItem as Site;

    /// <summary>
    /// The location of the ZIP file with the Dynamicweb application used for this site.
    /// </summary>
    public string DwZipSourceFile
    {
      get
      {
        if (dwVersion.SelectedItem == null)
        {
          return string.Empty;
        }
        return dwVersion.SelectedItem.ToString();
      }
    }

    public string DwZipSourceFileForVersionUpgrade
    {
      get
      {
        if (dwVersionForUpgrade.SelectedItem == null)
        {
          return string.Empty;
        }
        return dwVersionForUpgrade.SelectedItem.ToString();
      }
    }

    /// <summary>
    /// The name and host header of the new site.
    /// </summary>
    public string SiteName
    {
      get { return siteName.Text; }
    }

    /// <summary>
    /// The SQL Server instance.
    /// </summary>
    public string SqlServerInstance
    {
      get { return sqlServerInstance.Text; }
    }

    /// <summary>
    /// Determines whether to use the application pool "Dynamicweb 8 Sites" or to create a new pool named after the site.
    /// </summary>
    public bool UseDefaultAppPool
    {
      get { return rdStandardAppPool.Checked; }
    }

    /// <summary>
    /// Gets a value that determines if the password of the Administrator account should be reset during installation.
    /// </summary>
    public bool ResetPassword
    {
      get { return rdResetPassword.Checked; }
    }

    /// <summary>
    /// Whether or not to disable all existing scheduled tasks. 
    /// </summary>
    public bool DisableScheduledTasks
    {
      get { return rdDisableScheduledTasks.Checked; }
    }

    /// <summary>
    /// Gets a value that determines if data should be anonymized.
    /// </summary>
    public bool AnonymizeData
    {
      get { return rdAnonymizeData.Checked; }
    }

    /// <summary>
    /// Gets a value that determines if user passwords should be updated with a random value.
    /// </summary>
    public bool RandomizeUserPasswords
    {
      get { return rdRandomizeUserPasswords.Checked; }
    }

    public bool UseStandardWebConfig
    {
      get { return TakeAppsWebConfig.Checked; }
    }

    internal ReplacementBehavior AnonymizationMethod
    {
      get { return (ReplacementBehavior)AnonymizationMethodList.SelectedItem; }
    }

    internal string AnonymizationSuffix
    {
      get { return AnonymizeSuffix.Text; }
    }

    /// <summary>
    /// Determines whether to add a record to the hosts file that links the new site name to 127.0.0.1. Useful in off-line scenarios.
    /// </summary>
    public bool UpdateHostsFile
    {
      get { return cbUpdateHostsFile.Checked; }
    }

    /// <summary>
    /// Determines the type of solution to create.
    /// </summary>
    public SolutionType SolutionType
    {
      get
      {
        if (rdCustom.Checked)
        {
          return SolutionType.Custom;
        }
        return SolutionType.Standard;
      }
    }

    /// <summary>
    /// Determines the type of custom source; can be None, or a ZIP file or a folder.
    /// </summary>
    public CustomSource CustomSource
    {
      get
      {
        if (!rdCustomSource.Checked)
        {
          return CustomSource.None;
        }
        if (useZip.Checked)
        {
          return CustomSource.ZipFile;
        }
        return CustomSource.Folder;
      }
    }

    /// <summary>
    /// Determines the type of site to create.
    /// </summary>
    public SiteType SiteType
    {
      get
      {
        if (rdEmptySite.Checked)
        {
          return SiteType.Blank;
        }
        return SiteType.Existing;
      }
    }

    /// <summary>
    /// Determines if the site has a custom Application folder.
    /// </summary>
    public bool HasCustomApplicationFolder
    {
      get { return chkHasApplicationFolder.Checked; }
    }

    /// <summary>
    /// Determines if the site has a custom Files folder.
    /// </summary>
    public bool HasCustomFilesFolder
    {
      get { return chkHasFilesFolder.Checked; }
    }

    /// <summary>
    /// Determines if the site has a custom SQL Server database.
    /// </summary>
    public bool HasCustomSqlDatabase
    {
      get { return chkHasBackupDatabase.Checked; }
    }

    /// <summary>
    /// The name of the custom SQL Server database.
    /// </summary>
    public string CustomSqlDatabase
    {
      get { return singleDatabaseLabel.Visible ? singleDatabaseLabel.Text.Substring(1, singleDatabaseLabel.Text.Length - 2) : multipleDatabases.SelectedItem != null ? multipleDatabases.SelectedItem.ToString() : string.Empty; }
    }

    /// <summary>
    /// The path to a ZIP file or folder, containing custom data.
    /// </summary>
    public string CustomSourceLocation
    {
      get { return customZipFileOrFolder.Text; }
    }

    /// <summary>
    /// Gets a value that determines of Rizzo customizations have been selected.
    /// </summary>
    public bool CustomizeRizzo
    {
      get { return CheckRizzo.Checked; }
    }

    #endregion

    private void createSite_Click(object sender, EventArgs e)
    {
      if (rdCustomSource.Checked && string.IsNullOrEmpty(customZipFileOrFolder.Text))
      {
        MessageBox.Show("Please select a custom ZIP file or folder as the source for the application, files and database.");
        return;
      }
      createSite.Enabled = false;
      using (var siteGenerator = new SiteGenerator())
      {
        if (siteGenerator.CreateSite(this, _rizzoForm))
        {
          Close();
        }
        else
        {
          createSite.Enabled = true;
        }
      }
    }

    private void MainForm_Load(object sender, EventArgs e)
    {
      singleDatabaseLabel.Text = string.Empty;
      SetCustomHeight();
      LoadApplications();
      SetSqlServerInstance();
      SetControlDefaults();
      ListExistingSites();
      statusLabel.Text = "Ready";
      SetDefaultDomainName();
      SetVersionInfo();
      InitializeVersionInfoTab();
      InitializeRefreshSiteTab();
      LoadAnonymizationMethods();
    }

    private void LoadAnonymizationMethods()
    {
      LoadInList(AnonymizationMethodList);
      LoadInList(AnonymizationMethodOnBackup);
      AnonymizationSuffixOnBackup.Visible = false;
    }

    private void LoadInList(ComboBox combo)
    {
      combo.Items.Clear();
      combo.Items.Add(ReplacementBehavior.Random);
      combo.Items.Add(ReplacementBehavior.Suffix);
      combo.SelectedIndex = 1;

    }

    private void InitializeRefreshSiteTab()
    {
      siteListForRefresh.Items.Clear();
      siteListForRefresh.SelectedIndex = -1;
      siteListForRefresh.Text = string.Empty;

      var sites = SiteInfoProvider.GetSiteModels();
      foreach (var item in sites)
      {
        siteListForRefresh.Items.Add(item);
      }
    }

    private void InitializeVersionInfoTab()
    {
      siteListForVersionInfo.Items.Clear();
      siteListForVersionInfo.SelectedIndex = -1;
      siteListForVersionInfo.Text = string.Empty;
      var sites = SiteInfoProvider.GetSiteModels();
      foreach (var item in sites)
      {
        siteListForVersionInfo.Items.Add(item);
      }
    }

    private void SetVersionInfo()
    {
      Version version = Assembly.GetEntryAssembly().GetName().Version;
      VersionNumberLabel.Text = version.ToString();
    }

    private void ListExistingSites()
    {
      sitesToDelete.Items.Clear();
      sitesToDelete.SelectedIndex = -1;
      sitesToDelete.Text = string.Empty;
      SitesForUpgrade.Items.Clear();
      SitesForUpgrade.SelectedIndex = -1;
      SitesForUpgrade.Text = string.Empty;

      foreach (Site site in SiteInfoProvider.GetSites())
      {
        sitesToDelete.Items.Add(site);
        SitesForUpgrade.Items.Add(site);
      }
    }

    /// <summary>
    /// Resets the form to the default height when Use custom settings is unchecked.
    /// </summary>
    private void SetStandardHeight()
    {
      Height = 370;
      mainTabs.Height = 300;
    }

    /// <summary>
    /// Resizes the form to show the custom controls when Use custom settings is checked.
    /// </summary>
    private void SetCustomHeight()
    {
      Height = 510;
      mainTabs.Height = 440;
    }

    /// <summary>
    /// Loads all application ZIPs found in the ApplicationZipsFolder in a drop down list.
    /// </summary>
    private void LoadApplications()
    {
      string baseFolder = Configuration.GetAppSetting("ApplicationZipsFolder");
      if (!Directory.Exists(baseFolder))
      {
        MessageBox.Show(string.Format("The folder {0} does not exist. Create this folder and copy at least one Dynamicweb application ZIP file into this folder.\r\n\r\nExiting now...", baseFolder));
        System.Windows.Forms.Application.Exit();
      }
      var files = Directory.GetFiles(baseFolder, "*.zip").OrderBy(x => x);
      foreach (string file in files)
      {
        dwVersion.Items.Add(file);
        dwVersionForUpgrade.Items.Add(file);
      }
      if (dwVersion.Items.Count > 0)
      {
        dwVersion.SelectedIndex = dwVersion.Items.Count - 1;
        dwVersionForUpgrade.SelectedIndex = dwVersionForUpgrade.Items.Count - 1;
      }
    }

    /// <summary>
    /// Assigns the initial domain naming by combining today's date and time with the default domain name in app.config.
    /// </summary>
    private void SetDefaultDomainName()
    {
      if (string.IsNullOrEmpty(siteName.Text))
      {
        string today = string.Format("{0}{1}{2}{3}{4}", DateTime.Now.Year, DateTime.Now.Month.ToString(CultureInfo.InvariantCulture).PadLeft(2, '0'), DateTime.Now.Day.ToString(CultureInfo.InvariantCulture).PadLeft(2, '0'), DateTime.Now.Hour.ToString(CultureInfo.InvariantCulture).PadLeft(2, '0'), DateTime.Now.Minute.ToString(CultureInfo.InvariantCulture).PadLeft(2, '0'));
        siteName.Text = string.Format("{0}.{1}", today, Configuration.GetAppSetting("DefaultDomain"));
        siteName.SelectionStart = 0;
        siteName.SelectionLength = today.Length;
        siteName.Select();
      }
    }

    private void SetSqlServerInstance()
    {
      sqlServerInstance.Text = Configuration.GetAppSetting("SqlServerInstance");
    }

    private void SetControlDefaults()
    {
      cbUpdateHostsFile.Checked = Configuration.PreSelectUpdateHostsFile;
      rdStandardAppPool.Checked = Configuration.PreSelectUseStandardAppPool;
      TakeAppsWebConfig.Checked = Configuration.PreSelectTakeAppsWebConfig;
    }

    private void browseFilesFolder_Click(object sender, EventArgs e)
    {
      if (useZip.Checked)
      {
        if (openFileDialog1.ShowDialog() != DialogResult.Cancel)
        {
          customZipFileOrFolder.Text = openFileDialog1.FileName;
        }
      }
      else
      {
        if (folderBrowserDialog1.ShowDialog() != DialogResult.Cancel)
        {
          customZipFileOrFolder.Text = folderBrowserDialog1.SelectedPath;
        }
      }
    }

    private void customZipFileOrFolder_TextChanged(object sender, EventArgs e)
    {
      HandleCustomSource();
    }

    private void HandleCustomSource()
    {
      if (string.IsNullOrEmpty(customZipFileOrFolder.Text) || (!File.Exists(customZipFileOrFolder.Text) && !Directory.Exists(customZipFileOrFolder.Text)))
      {
        ClearCustomSettings();
        createSite.Enabled = false;
        return;
      }
      ConfigureCustom.Visible = true;
      createSite.Visible = true;

      bool hasApplicationFolder;
      bool hasFilesFolder;
      List<string> databaseBackups;

      if (useZip.Checked)
      {
        using (ZipArchive archive = ZipFile.OpenRead(customZipFileOrFolder.Text))
        {
          databaseBackups = archive.Entries.Where(x => x.FullName.ToLower().EndsWith(".bak") && !x.FullName.Contains("/")).Select(x => x.Name).ToList(); // Find files in the root only; don't accept a \ which means no .bak files in sub folders.
          hasApplicationFolder = archive.Entries.Any(x => x.FullName.ToLower().StartsWith("application/") || x.FullName.ToLower().StartsWith(@"application\"));
          hasFilesFolder = archive.Entries.Any(x => x.FullName.ToLower().StartsWith("files/globalsettings.aspx") || x.FullName.ToLower().StartsWith(@"files\globalsettings.aspx"));
        }
      }
      else
      {
        databaseBackups = Directory.GetFiles(customZipFileOrFolder.Text, "*.bak").Select(x => Path.GetFileName(x)).ToList();
        hasApplicationFolder = Directory.Exists(string.Format("{0}\\{1}", customZipFileOrFolder.Text, "Application"));
        hasFilesFolder = File.Exists(string.Format("{0}\\{1}\\GlobalSettings.aspx", customZipFileOrFolder.Text, "Files"));
      }

      if (databaseBackups.Any())
      {
        chkHasBackupDatabase.Checked = true;
        if (databaseBackups.Count() == 1)
        {
          singleDatabaseLabel.Visible = true;
          multipleDatabases.Visible = false;
          singleDatabaseLabel.Text = string.Format("({0})", databaseBackups.Single());
        }
        else
        {
          singleDatabaseLabel.Visible = false;
          multipleDatabases.Visible = true;
          foreach (var backup in databaseBackups)
          {
            multipleDatabases.Items.Add(backup);
          }
        }
      }
      else
      {
        singleDatabaseLabel.Visible = false;
        multipleDatabases.Visible = false;
        chkHasBackupDatabase.Checked = false;
      }

      chkHasBackupDatabase.Tag = chkHasBackupDatabase.Checked;

      chkHasApplicationFolder.Checked = hasApplicationFolder;
      chkHasApplicationFolder.Tag = hasApplicationFolder;
      if (hasApplicationFolder)
      {
        rdCustom.Checked = true;
        solutionType.Enabled = false;
      }
      else
      {
        solutionType.Enabled = true;
      }
      chkHasFilesFolder.Checked = hasFilesFolder;
      chkHasFilesFolder.Tag = hasFilesFolder;



      HandleRizzo();
    }

    private void HandleRizzo()
    {
      bool hasRizzo = false;
      try
      {
        using (ZipArchive archive = ZipFile.OpenRead(customZipFileOrFolder.Text))
        {
          hasRizzo = archive.Entries.Any(x => (x.FullName.ToLower().StartsWith("files\\globalsettings.aspx") || x.FullName.ToLower().StartsWith("files/globalsettings.aspx")));
        }
      }
      catch
      {
        // Ignore.        
      }
      GroupRizzo.Visible = hasRizzo;
      _rizzoForm = null;
    }

    private void ClearCustomSettings()
    {
      chkHasApplicationFolder.Checked = false;
      chkHasFilesFolder.Checked = false;
      chkHasBackupDatabase.Checked = false;
      singleDatabaseLabel.Text = string.Empty;
      multipleDatabases.Items.Clear();
      multipleDatabases.Text = string.Empty;
      multipleDatabases.Visible = false;
    }

    public void LogProgress(string message)
    {
      statusLabel.Text = message;
      Refresh();
    }

    private void mainTabs_SelectedIndexChanged(object sender, EventArgs e)
    {
      var tag = mainTabs.SelectedTab.Tag;
      Text = tag?.ToString() ?? "";
    }

    private void ClearCustomFileOrFolder()
    {
      customZipFileOrFolder.Text = string.Empty;
      SetFileOrFolderContentsText();
    }

    private void useZip_CheckedChanged(object sender, EventArgs e)
    {
      ClearCustomFileOrFolder();
      SetFileOrFolderContentsText();
    }

    private void useFolder_CheckedChanged(object sender, EventArgs e)
    {
      ClearCustomFileOrFolder();
    }

    private void SetFileOrFolderContentsText()
    {
      if (useZip.Checked)
      {
        fileOrFolderContents.Text = "File contents";
      }
      else
      {

        if (useFolder.Checked)
        {
          fileOrFolderContents.Text = "Folder contents";
        }
      }
    }

    private void DeleteButton_Click(object sender, EventArgs e)
    {
      DeleteButton.Enabled = false;
      try
      {
        var foldersToIgnore = _ignoreFolderDialog != null ? _ignoreFolderDialog.FoldersToIgnore : new string[0];
        UseWaitCursor = true;
        Site site = (Site)sitesToDelete.SelectedItem;
        if (site == null)
        {
          MessageBox.Show("Please select a site first.");
          DeleteButton.Enabled = true;
          return;
        }

        //get checked radio button and set it in selectedChoice
        var selectedRadioChoice = tabDeleteSite.Controls.OfType<RadioButton>().First(r => r.Checked).Name;
        var action = (BackupDeleteType)Enum.Parse(typeof(BackupDeleteType), selectedRadioChoice);

        var replacementBehavior = rdAnonymizeDataOnBackup.Checked ? (ReplacementBehavior)AnonymizationMethodOnBackup.SelectedItem : (ReplacementBehavior?)null;
        new SiteDeleter(this, site).BackupOrDeleteSite(action, replacementBehavior, AnonymizationSuffixOnBackup.Text, foldersToIgnore, skipBackingupDatabase.Checked);

        if (openFileExplorerWhenDone.Checked)
        {
          Process.Start(Configuration.GetAppSetting("BackupFolder"));
          Activate();
        }

        ListExistingSites();
        InitializeRefreshSiteTab();
        InitializeVersionInfoTab();
        DeleteButton.Enabled = true;
      }
      finally
      {
        UseWaitCursor = false;
      }
    }

    private void BackupOnly_CheckedChanged_1(object sender, EventArgs e)
    {
      siteContainer.Text = "Select site to backup";
      DeleteButton.Text = "Backup";
      openFileExplorerWhenDone.Enabled = true;
      EnableDisableAnonymizeDataOnBackup(true);
      EnableDisableIgnoreFolderPicker(true);
      EnableDisableSkipDatabase(true);
    }

    private void EnableDisableSkipDatabase(bool enabled)
    {
      skipBackingupDatabase.Enabled = enabled;
      if (!enabled)
      {
        skipBackingupDatabase.Checked = false;
      }
    }

    private void BackupAndDelete_CheckedChanged(object sender, EventArgs e)
    {
      siteContainer.Text = "Select site to backup and delete";
      DeleteButton.Text = "Backup and delete";
      openFileExplorerWhenDone.Enabled = true;
      EnableDisableAnonymizeDataOnBackup(true);
      EnableDisableIgnoreFolderPicker(true);
      EnableDisableSkipDatabase(false);
    }

    private void DeleteOnly_CheckedChanged(object sender, EventArgs e)
    {
      siteContainer.Text = "Select site to delete";
      DeleteButton.Text = "Delete";
      openFileExplorerWhenDone.Checked = false;
      openFileExplorerWhenDone.Enabled = false;
      rdAnonymizeDataOnBackup.Checked = false;
      EnableDisableAnonymizeDataOnBackup(false);
      EnableDisableIgnoreFolderPicker(false);
      EnableDisableSkipDatabase(false);
    }

    private void EnableDisableAnonymizeDataOnBackup(bool enable)
    {
      rdAnonymizeDataOnBackup.Enabled = enable;
      AnonymizationMethodOnBackup.Enabled = enable;
      AnonymizationMethodOnBackupLabel.Enabled = enable;
    }
    private void EnableDisableIgnoreFolderPicker(bool enable)
    {
      folderIgnoreSetup.Visible = enable;
    }

    private void ConfigureCustom_Click(object sender, EventArgs e)
    {
      var settingsForm = new ConfigureCustomSettings();
      settingsForm.ZipHasCustomApplication = (bool)chkHasApplicationFolder.Tag;
      settingsForm.ZipHasCustomFiles = (bool)chkHasFilesFolder.Tag;
      settingsForm.ZipHasCustomDatabase = (bool)chkHasBackupDatabase.Tag;
      settingsForm.Initialize();

      var showDialog = settingsForm.ShowDialog();
      if (showDialog == DialogResult.OK)
      {
        var customSourceSettings = settingsForm.CustomSourceSettings;
        switch (customSourceSettings.ApplicationSource)
        {
          case CustomSetupApplicationType.UseFromZip:
            chkHasApplicationFolder.Checked = true;
            rdStandard.Checked = false;
            rdCustom.Checked = false;
            break;
          case CustomSetupApplicationType.UseStandardDynamicwebApp:
            chkHasApplicationFolder.Checked = false;
            rdStandard.Checked = true;
            break;
          case CustomSetupApplicationType.CreateEmptyCustom:
            chkHasApplicationFolder.Checked = false;
            rdStandard.Checked = true;
            rdCustom.Checked = true;
            break;
        }

        switch (customSourceSettings.DatabaseSource)
        {
          case CustomSetupSourceType.UseFromZip:
            chkHasBackupDatabase.Checked = true;
            rdEmptySite.Checked = false;
            rdCustomSource.Checked = false;
            break;
          case CustomSetupSourceType.UseFromDefaultEmptyData:
            chkHasBackupDatabase.Checked = false;
            rdEmptySite.Checked = true;
            break;
        }

        switch (customSourceSettings.FilesSource)
        {
          case CustomSetupSourceType.UseFromZip:
            chkHasFilesFolder.Checked = true;
            break;
          case CustomSetupSourceType.UseFromDefaultEmptyData:
            chkHasFilesFolder.Checked = false;
            break;
        }
      }
    }

    private void TestConnection_Click(object sender, EventArgs e)
    {
      TestConnection.Enabled = false;
      Cursor.Current = Cursors.WaitCursor;
      TestConnectionToSql();
      Cursor.Current = Cursors.Default;
      TestConnection.Enabled = true;
    }

    private void TestConnectionToSql()
    {
      string sql = "SELECT HAS_PERMS_BY_NAME(null, null, 'CREATE ANY DATABASE')";
      using (var sqlConnection = new SqlConnection(string.Format("data source={0}; integrated security=true;", sqlServerInstance.Text)))
      {
        using (var sqlCommand = new SqlCommand(sql, sqlConnection))
        {
          try
          {
            sqlConnection.Open();
            var hasPermissions = Convert.ToInt32(sqlCommand.ExecuteScalar());
            if (hasPermissions == 1)
            {
              MessageBox.Show("Connection successful. Your account has permissions to create a database.");
            }
            else
            {
              MessageBox.Show("Connection failed. Your account does NOT have permissions to create a new database. You should run this tool using the Windows account that has permissions to create new databases in the SQL Server instance.");
            }
          }
          catch (Exception ex)
          {
            MessageBox.Show(string.Format("Error testing the connection. Make sure the SQL Server instance exists and that you have permissions to access it. The error message is: {0}.", ex.Message));
          }
        }
      }
    }

    private void HandleCustomChanged(object sender, EventArgs e)
    {
      solutionType.Enabled = true;
      if (rdCustomSource.Checked)
      {
        SetCustomHeight();
        customZipFileOrFolder.Enabled = true;
      }
      else
      {
        customZipFileOrFolder.Enabled = false;
        ClearCustomSettings();
        customZipFileOrFolder.Text = string.Empty;
        createSite.Enabled = true;
        SetStandardHeight();
      }
    }

    private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
    {
      Process.Start("https://dwinstaller.codeplex.com/documentation?referringTitle=Home");
    }

    private void ConfigureRizzo_Click(object sender, EventArgs e)
    {
      if (_rizzoForm == null)
      {
        _rizzoForm = new ConfigureRizzoForm { CustomZipFileOrFolder = customZipFileOrFolder.Text };
        _rizzoForm.Initialize();
      }
      _rizzoForm.ShowDialog();
    }

    private void CheckCustomizeRizzoTypes_CheckedChanged(object sender, EventArgs e)
    {
      ConfigureRizzo.Enabled = CheckRizzo.Checked;
    }

    private void siteListForVersionInfo_SelectedIndexChanged(object sender, EventArgs e)
    {
      var site = siteListForVersionInfo.SelectedItem as DynamicwebSite;
      propertyGrid1.Visible = true;
      propertyGrid1.SelectedObject = site;
      propertyGrid1.ResizeDescriptionArea(3);
      propertyGrid1.ExpandAllGridItems();
      propertyGrid1.SetLabelColumnWidth(140);
    }

    private void ShowUnusedApps_Click(object sender, EventArgs e)
    {
      var uniquePaths = SiteInfoProvider.GetUniqueApplicationPaths();
      var existingApps = Directory.GetDirectories(Configuration.GetAppSetting("StandardAppFolder")).Where(x => x.ToLower().Contains("application("));

      var result = existingApps.Except(uniquePaths, StringComparer.CurrentCultureIgnoreCase);

      var viewer = new TextViewer
      {
        Lines = result,
        Text = "Unused application folders"
      };
      viewer.Show();
    }

    private void ShowUnusedZips_Click(object sender, EventArgs e)
    {
      var uniquePaths = SiteInfoProvider.GetUniqueApplicationPaths();
      var existingZips = Directory.GetFiles(Configuration.GetAppSetting("ApplicationZipsFolder"));

      var uniqueAppNames = uniquePaths.Select(x => new DirectoryInfo(x).Name);
      var existingZipNames = existingZips.Select(x => Path.GetFileNameWithoutExtension(x));

      var result = existingZipNames.Except(uniqueAppNames, StringComparer.CurrentCultureIgnoreCase);

      var viewer = new TextViewer
      {
        Lines = result,
        Text = "Unused ZIP files"
      };
      viewer.Show();
    }

    private void HandleRefreshSource()
    {
      if (string.IsNullOrEmpty(zipForRefresh.Text) || !File.Exists(zipForRefresh.Text))
      {
        ClearCustomSettings();
        RefreshSiteButton.Enabled = false;
        return;
      }
      RefreshOptionsContainer.Visible = true;
      RefreshSiteButton.Enabled = true;

      bool hasApplicationFolder;
      bool hasFilesFolder;
      List<string> databaseBackups;

      using (var archive = ZipFile.OpenRead(zipForRefresh.Text))
      {
        databaseBackups = archive.Entries.Where(x => x.FullName.ToLower().EndsWith(".bak") && !x.FullName.Contains("/")).Select(x => x.Name).ToList(); // Find files in the root only; don't accept a \ which means no .bak files in sub folders.
        hasApplicationFolder = archive.Entries.Any(x => x.FullName.ToLower().StartsWith("application/") || x.FullName.ToLower().StartsWith(@"application\"));
        hasFilesFolder = archive.Entries.Any(x => x.FullName.ToLower().StartsWith("files/globalsettings.aspx") || x.FullName.ToLower().StartsWith(@"files\globalsettings.aspx"));
      }

      if (databaseBackups.Any())
      {
        RefreshHasDatabase.Enabled = true;
        if (databaseBackups.Count == 1)
        {
          RefreshSingleDatabase.Visible = true;
          RefreshAvailableDatabases.Visible = false;
          RefreshSingleDatabase.Text = $"({databaseBackups.Single()})";
        }
        else
        {
          RefreshSingleDatabase.Visible = false;
          RefreshAvailableDatabases.Visible = true;
          foreach (var backup in databaseBackups)
          {
            RefreshAvailableDatabases.Items.Add(backup);
          }
        }
      }
      else
      {
        RefreshSingleDatabase.Visible = false;
        RefreshAvailableDatabases.Visible = false;
        RefreshHasDatabase.Enabled = false;
      }

      RefreshHasApp.Enabled = hasApplicationFolder;
      RefreshHasFiles.Enabled = hasFilesFolder;
      checkRenameFileInsteadOfDelete.Enabled = false;
      checkRenameFileInsteadOfDelete.Checked = false;
    }

    private void zipForRefresh_TextChanged(object sender, EventArgs e)
    {
      HandleRefreshSource();
    }

    private void SelectRefresh_Click_1(object sender, EventArgs e)
    {
      if (openFileDialog1.ShowDialog() != DialogResult.Cancel)
      {
        zipForRefresh.Text = openFileDialog1.FileName;
      }
    }

    private void RefreshSiteButton_Click(object sender, EventArgs e)
    {
      if (!(RefreshHasDatabase.Checked || RefreshHasApp.Checked || RefreshHasFiles.Checked))
      {
        MessageBox.Show("Please select at least one component to refresh.");
        return;
      }
      if (siteListForRefresh.SelectedItem == null)
      {
        MessageBox.Show("Please select a site to refresh.");
        return;
      }
      var dwSite = (DynamicwebSite)siteListForRefresh.SelectedItem;
      var site = SiteInfoProvider.GetSiteById(dwSite.Id);
      RefreshSite(site, zipForRefresh.Text, RefreshHasApp.Checked, RefreshHasFiles.Checked, RefreshHasDatabase.Checked, checkCreateBackup.Checked, checkRenameFileInsteadOfDelete.Checked);
    }

    private void RefreshSite(Site site, string zipFile, bool refreshApplication, bool refreshFiles, bool refreshDatabase, bool createBackup, bool renameFilesFolder)
    {
      if (createBackup)
      {
        new SiteDeleter(this, site).BackupSite();
      }
      using (var siteGenerator = new SiteGenerator())
      {
        var database = RefreshSingleDatabase.Visible ? RefreshSingleDatabase.Text.Substring(1, RefreshSingleDatabase.Text.Length - 2) : RefreshAvailableDatabases.SelectedItem != null ? RefreshAvailableDatabases.SelectedItem.ToString() : string.Empty;

        if (siteGenerator.RefreshSite(site, zipFile, refreshApplication, refreshFiles, refreshDatabase, database, renameFilesFolder, this))
        {
          Close();
        }
        else
        {
          RefreshSiteButton.Enabled = true;
        }
      }
    }

    private void RefreshHasFiles_CheckedChanged(object sender, EventArgs e)
    {
      checkRenameFileInsteadOfDelete.Enabled = RefreshHasFiles.Checked;
      checkRenameFileInsteadOfDelete.Checked = RefreshHasFiles.Checked;
    }

    private void rdAnonymizeData_CheckedChanged(object sender, EventArgs e)
    {
      MethodLabel.Visible = rdAnonymizeData.Checked;
      AnonymizationMethodList.Visible = rdAnonymizeData.Checked;
      AnonymizeSuffix.Visible = rdAnonymizeData.Checked && rdAnonymizeData.Checked && AnonymizationMethodList.SelectedItem?.ToString() == "Suffix"; 
    }

    private void AnonymizeDataOnBackup_CheckedChanged(object sender, EventArgs e)
    {
      AnonymizationMethodOnBackupLabel.Visible = rdAnonymizeDataOnBackup.Checked;
      AnonymizationMethodOnBackup.Visible = rdAnonymizeDataOnBackup.Checked;
      AnonymizationSuffixOnBackup.Visible = rdAnonymizeDataOnBackup.Checked && AnonymizationMethodOnBackup.SelectedItem?.ToString() == "Suffix";
    }

    private void AnonymizationMethodList_SelectedIndexChanged(object sender, EventArgs e)
    {
      AnonymizeSuffix.Visible = AnonymizationMethodList.SelectedItem?.ToString() == "Suffix";
    }

    private void AnonymizationMethodOnBackup_SelectedIndexChanged(object sender, EventArgs e)
    {
      AnonymizationSuffixOnBackup.Visible = AnonymizationMethodOnBackup.SelectedItem?.ToString() == "Suffix";
    }

    private void folderIgnoreSetup_Click(object sender, EventArgs e)
    {
      _ignoreFolderDialog.Show();
    }

    private void sitesToDelete_SelectedIndexChanged(object sender, EventArgs e)
    {
      EnableDisableIgnoreFolderPicker(sitesToDelete.SelectedItem != null);
      var site = sitesToDelete.SelectedItem as Site;
      _ignoreFolderDialog.SiteToBackup = site;
    }

    private void sitesToDelete_TextChanged(object sender, EventArgs e)
    {
      EnableDisableIgnoreFolderPicker(sitesToDelete.SelectedItem != null);
    }

    private void UpgradeDynamicwebVersion_Click(object sender, EventArgs e)
    {
      UpgradeDynamicwebVersion.Enabled = false;
      try
      {
        new SiteGenerator().UpdateVersion(this);
      }
      finally
      {
        UpgradeDynamicwebVersion.Enabled = true;
      }
    }

    private void ShowIssues_Click(object sender, EventArgs e)
    {
      var result = "";
      var sites = SiteInfoProvider.GetSiteModels();
      foreach (var site in sites)
      {
        var webConfig = Path.Combine(site.RootPath, "web.config");
        var xmlDocument = new XmlDocument();
        xmlDocument.Load(webConfig);
        var hasIssue = xmlDocument.SelectSingleNode("//location[@path='Files']/system.webServer/handlers/clear");
        if (hasIssue != null)
        {
          result += string.Format("{0}\r\n", webConfig);
        }
      }
      if (string.IsNullOrEmpty(result))
      {
        MessageBox.Show("No sites with issues.");
      }
      else
      {
        MessageBox.Show("The following sites have an issue with web.config: \r\n\r\n" + result);
      }
    }
  }
}
